package edu.uba.fcen.estimacion.word.selection;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import edu.uba.fcen.estimacion.word.selection.constants.Constants;

public class DefinitiveWords {
	private static Logger logger = Logger.getLogger(DefinitiveWords.class); 
	// the key is the lemma and it doesn't matter the value
	private Map<String, Integer> words = new HashMap<String, Integer>();
	private Map<String, Integer> wordNum = new HashMap<String, Integer>();
	
	public void execute() {
		File directory = new File(Constants.PATH_FREELING_DECORATE_FILE_TALES);
		long initial, end;
		initial = System.currentTimeMillis();
		this.buildMapWordNum();
		for(File in : directory.listFiles()) {
			if (in.getName().startsWith("out")) {
				logger.debug("Cleaning file: " + in.getName());
				this.clean(in);
			}
		}
		end = System.currentTimeMillis();
		logger.debug("Demoro: " + (end - initial));
		this.writeResult();
		logger.debug("There are " + words.size() + " differents words");
	}
	
	private void buildMapWordNum() {
		Reader in;
		try {
			FileInputStream fileInputStream = new FileInputStream(Constants.PATH_FREELING_DECORATE_FILE_TALES + Constants.FILE_ORDERED_BY_NUM);
			in = new InputStreamReader(fileInputStream, Constants.ENCODING);
			BufferedReader br = new BufferedReader(in);
			String line;
			String [] split;
			while ((line = br.readLine())!=null) {
				split = line.split("\t\t\t");
				wordNum.put(split[0], Integer.valueOf(split[1]));
			}
			in.close();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void writeResult() {
		Writer out;
		try {
			
			out = new OutputStreamWriter(new FileOutputStream(
					Constants.PATH_FREELING_DECORATE_FILE_TALES + Constants.DEFINITIVE_WORDS),
					Constants.ENCODING);
			BufferedWriter dicFile = new BufferedWriter(out);
			List<Pair<String, Integer>> sortedKeys = this.getSortedList();
			
			for (Pair<String, Integer> pair : sortedKeys) {
				dicFile.write(pair.getX() + "\t\t\t" + pair.getY() + System.getProperty("line.separator"));
			}
			dicFile.flush();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} 
	}


	private void clean(File file) {
		Reader in;
		try {
			in = new InputStreamReader(new FileInputStream(file), Constants.ENCODING);
			BufferedReader br = new BufferedReader(in);
			String line;
			while ((line = br.readLine())!=null) {
				clean(line);
			}
			in.close();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}


	private void clean(String line) {
		String[] words = line.split(" ");
		String word, lemma, tag;
		double probability = 0d; 
		for (int i = 1; i < words.length; i+=3) {
			word = words[0];
			if (word.length() > 2) {
				lemma = removeAfterPlus(words[i]);
				tag = words[i+1];
				probability = Double.valueOf(words[i+2]);
				if (isAllowed(tag) && !isNumber(lemma)) {
					String key = lemma+"_"+tag.charAt(0);
					if (isException(tag)) {
						addToDictionary(word, word, probability);
					} else {
						addToDictionary(word, key, probability);
					}
				}
			}
		}
	}

	private boolean isNumber(String lemma) {
		boolean isNumber = true;
		try {
			Integer.valueOf(lemma);
		} catch (NumberFormatException e) {
			isNumber = false;
		}
		return isNumber;
	}

	private String removeAfterPlus(String word) {
		int indexPlus = word.indexOf("+");
		String ret = word;
		if (indexPlus > 0) {
			ret = word.substring(0, indexPlus);
		}
		return ret;
	}

	/**
	 * Por ejemplo: los nombres comunes con aumentativo o diminutivo, como puede ser gatito.
	 * @param tag
	 * @return
	 */
	private boolean isException(String tag) {
		boolean ret = false;
		if (tag.startsWith("NC") && (tag.endsWith("D") || tag.endsWith("A"))) {
			ret = true;
		}
		return ret;
	}

	private void addToDictionary(String word, String key, double probability) {
		int cantidadApariciones = 0;
		//Elimino aquellas palabras como de que generan una key = d_N 
		if (key.length() > 4) {
			if (this.words.containsKey(key)) {
				cantidadApariciones = this.words.get(key);
			}
			if (this.wordNum.containsKey(word)) {
				this.words.put(key, (cantidadApariciones + (int) (this.wordNum.get(word)*probability)));
			} else {
				this.words.put(key, 1+cantidadApariciones);
			}
		}
	}


	private boolean isAllowed(String tag) {
		boolean isPronombre = tag.startsWith("P");
		boolean isDeterminante = tag.startsWith("D");
		boolean isInterjeccion = tag.startsWith("I");
		boolean isPreposicion = tag.startsWith("S");
		boolean isNombrePropio = tag.startsWith("NP");
		boolean isNum = tag.startsWith("Z");
		boolean isPuntuacion = tag.startsWith("F");
		boolean isDateAndHours = tag.startsWith("W");
		boolean isConjuncion = tag.startsWith("C");
		return !(isDeterminante || isInterjeccion || isPreposicion || isNum || isPuntuacion || isDateAndHours || isNombrePropio || isConjuncion || isPronombre);
	}
	
	private List<Pair<String, Integer>> getSortedList() {
		List<Pair<String, Integer>> sortedList = new ArrayList<Pair<String,Integer>>(words.size());
		
		for (String key : words.keySet()) {
			sortedList.add(new Pair<String, Integer>(key, words.get(key)));
		}
		Collections.sort(sortedList, new Comparator<Pair<String,Integer>>() {

			@Override
			public int compare(Pair<String,Integer> o1, Pair<String,Integer> o2) {
				return o2.getY().compareTo(o1.getY());
			}
		});
		return sortedList;
	}
	
	public static void main(String[] args) {
		DefinitiveWords dw = new DefinitiveWords();
		dw.execute();
	}
}
